﻿#include "MLibrary.h"

namespace LibraryViewer
{

bool MLibrary::Load = true;

    MLibrary::MLibrary(const std::string &filename)
    {
        FileName = filename;
        Initialize();
        Close();
    }

    void MLibrary::Initialize()
    {
        int CurrentVersion;
        _initialized = true;

        if (!FileSystem::fileExists(FileName))
        {
            return;
        }

        _stream = new FileStream(FileName, FileMode::Open, FileAccess::ReadWrite);
        _reader = new BinaryReader(_stream);
        CurrentVersion = _reader->ReadInt32();

        if (CurrentVersion < 2)
        {
            MessageBox::Show("Wrong version, expecting lib version: " + std::to_string(LibVersion) + " found version: " + std::to_string(CurrentVersion) + ".", "Failed to open", MessageBoxButtons::OK, MessageBoxIcon::Error, MessageBoxDefaultButton::Button1);
            return;
        }

        Count = _reader->ReadInt32();

        Images = std::vector<MImage*>();
        IndexList = std::vector<int>();

        int frameSeek = 0;
        if (CurrentVersion >= 3)
        {
            frameSeek = _reader->ReadInt32();
        }

        for (int i = 0; i < Count; i++)
        {
            IndexList.push_back(_reader->ReadInt32());
        }

        for (int i = 0; i < Count; i++)
        {
            Images.push_back(nullptr);
        }

        for (int i = 0; i < Count; i++)
        {
            CheckImage(i);
        }
    }

    void MLibrary::Close()
    {
        if (_stream != nullptr)
        {
            _stream->Dispose();
        }
    }

    void MLibrary::CheckImage(int index)
    {
        if (!_initialized)
        {
            Initialize();
        }

        if (Images.empty() || index < 0 || index >= Images.size())
        {
            return;
        }

        if (Images[index] == nullptr)
        {
            _stream->Position = IndexList[index];
            Images[index] = new MImage(_reader);
        }

        if (!Load)
        {
            return;
        }

        MImage *mi = Images[index];
        if (!mi->TextureValid)
        {
            _stream->Seek(IndexList[index] + 12, SeekOrigin::Begin);
            mi->CreateTexture(_reader);
        }
    }

    MLibrary::MImage *MLibrary::GetMImage(int index)
    {
        if (index < 0 || index >= Images.size())
        {
            return nullptr;
        }

        return Images[index];
    }

    Bitmap *MLibrary::GetPreview(int index)
    {
        if (index < 0 || index >= Images.size())
        {
            return new Bitmap(1, 1);
        }

        MImage *image = Images[index];

        if (image == nullptr || image->Image == nullptr)
        {
            return new Bitmap(1, 1);
        }

        if (image->Preview == nullptr)
        {
            image->CreatePreview();
        }

        return image->Preview;
    }

    void MLibrary::RemoveImage(int index)
    {
        if (Images.empty() || Count <= 1)
        {
            Count = 0;
            Images = std::vector<MImage*>();
            return;
        }
        Count--;

        Images.erase(Images.begin() + index);
    }

    MLibrary::MImage::MImage(BinaryReader *reader)
    {
        //read layer 1
        Width = reader->ReadInt16();
        Height = reader->ReadInt16();
        X = reader->ReadInt16();
        Y = reader->ReadInt16();
        ShadowX = reader->ReadInt16();
        ShadowY = reader->ReadInt16();
        Shadow = reader->ReadByte();
        Length = reader->ReadInt32();
        FBytes = reader->ReadBytes(Length);
        //check if there's a second layer and read it
        HasMask = ((Shadow >> 7) == 1) ? true : false;
        if (HasMask)
        {
            MaskWidth = reader->ReadInt16();
            MaskHeight = reader->ReadInt16();
            MaskX = reader->ReadInt16();
            MaskY = reader->ReadInt16();
            MaskLength = reader->ReadInt32();
            MaskFBytes = reader->ReadBytes(MaskLength);
        }
    }

    std::vector<unsigned char> MLibrary::MImage::Compress(std::vector<unsigned char> &raw)
    {
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (MemoryStream memory = new MemoryStream())
        {
            MemoryStream memory = MemoryStream();
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (GZipStream gzip = new GZipStream(memory, CompressionMode.Compress, true))
            {
                GZipStream gzip = GZipStream(memory, CompressionMode::Compress, true);
                gzip.Write(raw, 0, raw.size());
            }
            return memory.ToArray();
        }
    }

    std::vector<unsigned char> MLibrary::MImage::Decompress(std::vector<unsigned char> &gzip)
    {
        // Create a GZIP stream with decompression mode.
        // ... Then create a buffer and write into while reading from the GZIP stream.
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
        {
            MemoryStream tempVar(gzip);
            GZipStream stream = GZipStream(&tempVar, CompressionMode::Decompress);
            constexpr int size = 4096;
            std::vector<unsigned char> buffer(size);
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (MemoryStream memory = new MemoryStream())
            {
                MemoryStream memory = MemoryStream();
                int count = 0;
                do
                {
                    count = stream.Read(buffer, 0, size);
                    if (count > 0)
                    {
                        memory.Write(buffer, 0, count);
                    }
                } while (count > 0);
                return memory.ToArray();
            }
        }
    }

    void MLibrary::MImage::CreatePreview()
    {
        if (Image == nullptr)
        {
            Preview = new Bitmap(1, 1);
            return;
        }

        Preview = new Bitmap(64, 64);

//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (Graphics g = Graphics.FromImage(Preview))
        {
            Graphics g = Graphics::FromImage(Preview);
            g.InterpolationMode = InterpolationMode::Low; //HighQualityBicubic
            g.Clear(Color::Transparent);
            int w = std::min(static_cast<int>(Width), 64);
            int h = std::min(static_cast<int>(Height), 64);
            Rectangle tempVar((64 - w) / 2, (64 - h) / 2, w, h);
            Rectangle tempVar2(0, 0, Width, Height);
            g.DrawImage(Image, &tempVar, &tempVar2, GraphicsUnit::Pixel);

            g.Save();
        }
    }
}
